 /**
*  通信兵
* 含义就是把交给他的包（他不管包里是什么东西）送到服务器，并从服务器取回结果
* 自己保证通信的可靠性，如果超时（超时以后就不允许再接收）或者是失败
*最新添加了超时处理
*
*！！重大发现，原来setInterval失效是因为new Singnalman()没有被保存，而被垃圾收集干掉了!所以再执行
*setInterval()里被定时的函数时导致失效？
 * 
 * 三个重要元素
 * 1,此次时间的委托人 INetListere
 * 2，目的地 nc_dest
 * 3,消息包 myPacage
*/

package kgame5.kio.remoting {
import kgame5.kutil.*;

import flash.events.*;
import flash.net.*;
import flash.utils.*;

class Signalman{
	
	//__________________Property________________________
	//--通知外部用的
	private var sm:SignalmenManager;
	
	//--nc.call里用的
	private var servletPath:String;
	
	//--可以理解成通信的目的地
	private var nc_dest : NetConnection = null;
	
	//--要传递的信息,
	private var myPackage : Object = null;//注意flashRemoting认为一个有array_x[0]=0,array_x.ttt=""的数组是java的HashMap
	
	//----
	//失败次数记录
	private var failTimes : int = 0;
	
	//标志这个signalman是否可用，这个是用来在队列里检查找到一个能用的signalman来使用的
	private var isFree2:Boolean=true;
	
	//-
	private var bNeedAutoRetry:Boolean=true;//是否需要自动重连
	
	private var timer_comm:Timer;//计算通信时间,到时间还不通就认为超时
	private var timer_autoRetry:Timer;//等待自动重连
	
	private var MaxFailTimes:int=10;//最大失败次数，超过此次数就认为通信失败，小于次次数则要重试
	
	
	//__________________Method______________________
	public function Signalman (p_servletPath:String,p_sm:SignalmenManager){
		servletPath=p_servletPath;
		
		sm=p_sm;
		
		timer_comm=new Timer(5*60*1000);
		timer_autoRetry=new Timer(30*1000);
	}
	
	//
	public function SendAndLoad(p_dest : NetConnection, p_package : Object,
									p_bNeedAutoRetry:Boolean,
									Timeout_Dur:int,AutoRetry_Dur:int,p_MaxFailTimes:int
									){
		
		isFree2=false;//标志已经被使用
		
		failTimes=0;//失败次数清空,否则重用的时候会以为错了12次呢！
		
		nc_dest = p_dest;
		myPackage = p_package;
		
		//--设置自己参数
		bNeedAutoRetry=p_bNeedAutoRetry;
		MaxFailTimes=p_MaxFailTimes;
		timer_comm.delay=Timeout_Dur;//超时时间
		timer_autoRetry.delay=AutoRetry_Dur;//自动重连时间
		
		trace2("----------------!!!SendAndLoad启动通信!!!--------------------");
		
		//--
		comm ();
	}
	//清除一下，以方便下次使用
	private function clear(){
		myPackage=null;
		//自己计算连接超时的计时器
		timer_comm.stop();
		timer_comm.removeEventListener(TimerEvent.TIMER,timeout);
		//失败重连的计时器
		timer_autoRetry.stop();
		timer_autoRetry.removeEventListener(TimerEvent.TIMER,autoRetry);
		
		isFree2=true;//标志现在有空了
		
		bNeedAutoRetry=true;//默认是要自动重连的
	}
	//返回是否有空
	public function isFree(){
		return isFree2;
	}
	//----执行一次通信
	private function comm (){
		trace2("------comm建立一次通信------");
		     // 通过类名调用Servlet
     // 例如，如果Servlet引擎接收了来自 /servlet/com.foo.servlet.MailServlet的请求，Servlet引擎会载入这个 com.foo.servlet.MailServlet类，建立实例，并通过这个Servlet来处理请求。
		//nc.call("/.MyServlet",new Responder(onResult2,onFault2),createPackage(8))
		nc_dest.call (servletPath, new Responder(resultHandler,faultHandler),myPackage);
	
		//设置超时时间
		timer_comm.start();
		timer_comm.addEventListener(TimerEvent.TIMER,timeout);
	}
	//----
	private function resultHandler (re :Object){
		trace2("----------------!!!resultHandler通信成功 "+re.iPackageID+"!!!--------------------");
		
		//!!注意这里的if是很重要的东西啊！！他保证的是如果一个被判定为超时的包又回来了的处理情况
		//如果已经不是当前正发送的包回来了，则不处理，因为它早已被抛弃了！
		//如果是当前正在发送的包回来了（不关它是否是被判定超时了的，我的目的不就是要发个请求要响应吗，且是同一请求的响应，用哪个不都一样啊，用完了此signalmen任务完成，撤退了啊就，等待新的任务）
		if(myPackage!=null&&re.iPackageID==myPackage.iPackageID){
			//通知说通信成功
			sm.notifyCommOK (re);
			
			//通信完毕后销毁对象
			clear();
		}else{
			T.p("此packege是过期的了，直接丢弃哦");
		}
	}
	//----
	private function faultHandler (fe : Object){
		//Part 2:输出调试信息
		trace2("----------------!!!faultHandler remoting报错!!!--------------------");
		
		for(var i in fe){
			trace(fe[i]);
		}
		
		//内部先处理通信失败的情况
		handleCommFail();
	}
	//
	private function timeout(e:TimerEvent){
		trace2("-----timeout网络连接超时-----");
		
		//自己计算连接超时的计时器,计时到了就关闭
		timer_comm.stop();
		timer_comm.removeEventListener(TimerEvent.TIMER,timeout);
		
		//内部先处理通信失败的情况，就是在尝试10次
		handleCommFail();
		
		/*老的
		//网络连接超时，超过给定的时间仍没得到响应
		sm.notifyCommTimeout (myPackage);
		
		//通信完毕后销毁对象
		clear();
		//*/
	}
	
	//当通信失败的时候，注意可能是remoting报错也可能是网络通信时间超时
	private function handleCommFail(){
		if(bNeedAutoRetry){//如果需要出错自动重连则出错自动重连
			//Part 1:出错的处理
			if (failTimes ++ >= MaxFailTimes){
				//通信失败次数超过10次则已经尽了最大努力而通信仍然失败,同志司令官说通信失败
				sm.notifyCommFail (myPackage);
		
				//通信完毕后销毁对象
				clear();
			}else{
				//失败了等几分钟继续尝试通信
				timer_autoRetry.start();
				timer_autoRetry.addEventListener(TimerEvent.TIMER,autoRetry);
			}
		}else{
			trace2("-----非关键数据出错，直接关闭即可-----");
			//注意每次用完了清除资源是必须的，否则这个得不到释放会造成内存泄露
			clear();//销毁对象，释放资源
		}
	}
	//
	private function autoRetry(e:TimerEvent){
		trace2("-----Signalman自动重试------");
		//失败重连的计时器要清除
		timer_autoRetry.stop();
		timer_autoRetry.removeEventListener(TimerEvent.TIMER,autoRetry);
		//
		comm();
		
		//通知说在自动重连
		sm.notifyAutoRetry(myPackage);
	}
	
	//--------调试用的
	private function trace2(sTip:String){
		///-T.p(sTip,"pid:"+myPackage.iPackageID,"bNeedAutoRetry:"+bNeedAutoRetry,"fid:"+myPackage.iFuncID,"failTimes:"+failTimes);
	}
} //end of class
}